package model.camera;

import java.util.ArrayList;
import java.util.HashMap;

import model.OnScreen;
import model.Snippet;
import model.service.ServiceCalculator;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.multi.qrcode.QRCodeMultiReader;

import android.hardware.Camera.Size;
import android.os.AsyncTask;
import android.util.Log;

public class Decoder extends AsyncTask<byte[], Void, Result[]>
{
	private static final String TAG = Decoder.class.getSimpleName();
	
	//Fields
	private final QRCodeMultiReader _multiFormatReader = new QRCodeMultiReader();
	
	private Size _previewSize;
		
	//Constructor
	public Decoder(Size previewSize, byte[]... bytes) 
	{
		_previewSize = previewSize;
	}
	
	@Override
	protected Result[] doInBackground(byte[]... bytes) 
	{
		Thread.currentThread().setName(String.format("AsyncTask: %s", TAG));
		
		return checkBinaryBitmapForQRCodes(bytes);
	}
	
	/**
	 * Attempts to decode QR-codes currently present in frame.
	 * @param bytes The frame to decode
	 * @return A list of decoded data.
	 */
	private Result[] checkBinaryBitmapForQRCodes(byte[]...bytes)
	{
		Result[] results = null;
		
		final PlanarYUVLuminanceSource source = buildLuminanceSource(bytes[0]);
		
		if(source != null)
		{
			//Zxing library requires the use of a BinaryBitmap for decoding
			BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
			try 
			{
				results = _multiFormatReader.decodeMultiple(bitmap);
				if(results.length == 0)
					return null;
			} catch (ReaderException e) 
			{
				Log.d(TAG, String.format("No code found %s", e.toString()));
				return null;
			}
			finally {
				bitmap = null;
				_multiFormatReader.reset();
			}
		}
		
		return results;
	}

	private synchronized PlanarYUVLuminanceSource buildLuminanceSource(byte[] bytes)
	{
		return new PlanarYUVLuminanceSource(bytes, _previewSize.width, _previewSize.height, 0, 0, _previewSize.width, _previewSize.height, false);
	}
	
	@Override
	protected void onPostExecute(Result[] results) 
	{
//		ArrayList<ReferencePoint> refPoints = new ArrayList<ReferencePoint>();
		ArrayList<Result> acceptedResults = new ArrayList<Result>();
		
		if(hasResult(results))
		{
			Log.d(TAG, String.format("Results found: %d", results.length));
			for (Result result : results) 
			{
				if(result == null)
					continue;
				
				if(qrValueEqualsOurFormat(result.toString()))
						acceptedResults.add(result);
				else
				{
					OnScreen.getInstance().notifyScreenWithToast("visible code has wrong format");
					continue;
				}
			}

		}
		
		Log.d(TAG, String.format("correct results: %d", acceptedResults.size()));
		
		if(!acceptedResults.isEmpty() && acceptedResults.size() >= 2)
			ServiceCalculator.calculateClippingPane(acceptedResults);
		else
			OnScreen.getInstance().updateSnippetList(new HashMap<String,Snippet>());
		
		//OnScreen.getInstance().updateReferencePointList(refPoints);
		
		CameraManager.addCallbackBuffer();
	}
	
	private boolean hasResult(Result[] results)
	{
		if(results == null)
			return false;
		if(results.length == 1)
		{
			OnScreen.getInstance().notifyScreenWithToast("niet genoeg codes in beeld");
			return false;
		}
		
		return results.length >= 2;
	}
	
	private boolean qrValueEqualsOurFormat(String result)
	{
		for (char c : result.toCharArray())
	    {
	        if (Character.isDigit(c)) continue; // if the char is a digit -> go check the next char.
	        if(c != '_') return false;			// otherwise check if it is an underscore, if it is not an underscore, return false
	    }
		
		return result.split("_").length == 3;
	}
}
